<?php

/** dBase-III access.
 *  This collection of functions (libary) is licensed under GPL2.
 *  See LICENSE or www.gnu.org for more details.
 *  @author Henri Ranki
 */


include_once("soset.php");
include_once("sostring.php");

/** Class to access dBase-III files.
 *  @var array/str $__Cols Column names.
 *  @var int $__CurRec Current record number.
 *  @var bool $__DBaseId Table handler
 */
class sodBase{
    var $Table = ""; /*str Table name */

    /** @access private */
    var $__Cols;
    var $__CurRec = 0;
    var $__DBaseId = 0;


    /** constructor
     *  Open dBase table. If table does not exist it is created. In that case
     *  also column definitions must be suplied. See setCols() function
     *  for further information.
     *
     *  @param string $TableName  The table name (path and filename)
     *  @param Array/string $ColDefs Column definitions
     *  @return boolean    true if ok otherwise false
     *  @package sodbase
     */
    function sodBase($TableName, $ColDefs = "") {
        /* Check that table name is ok */
        if (!$TableName) return 0;     // <-- EXIT!
        
        $this->Table = $TableName;

        if (file_exists($TableName)) {
            /* Open table for reading and writing */
            $this->__DBaseId = dbase_open($this->Table, 2);
        } else {
            /* Table did not exist. If column definitions are given create table */
            if ($ColDefs) {
                $this->setCols($ColDefs);
                $this->__DBaseId = $this->createTable();
            } // if $ColDefs
        } // if file_exists($TableName)

        /* If table opened successfully set the first row as the current row */
        if (($this->__DBaseId) && dbase_numrecords($this->__DBaseId) > 0)
            $this->__CurRec = 1;

    } // constructor


    /** creates the dBase table
     *  Creates the table on the disk.
     *  @return int   dBase table id or false if en error occurred
     *  @package sodbase
     */
    function createTable() {
        /* Check that all needed information is given */
        if ((!$this->Table) &&
            (is_array($this->__Cols)) &&
            (count($this->__Cols) == 0)) {
            return false;                   // <-- EXIT!
        } // if

        $result = dbase_create($this->Table, $this->__Cols);
        $this->__DBaseId = $result;

        return $result;
    } // createTable()


    /** Set column names of the table
     * Columns are defined as folows:
     * Each item in the array defines one column. Colum definition consists
     * of column name, type and information depending on the type.
     * The definition is ':' separated string. e.g.
     * "name:str:50" defines a string that maximum length is 50 characters.
     * Types are listed in the table below:
     *
     *  str:<length>
     *  bool (no length)
     *  memo (no length)
     *  date (no length or precision). Format YYYYMMDD
     *  num:<length>:<precision>
     *
     *  @param array/string $ColDefs Column definitions
     *  @return boolean true if ok otherwise false
     *  @package sodbase
     */
    function setCols($ColDefs){
        $result = array();

        /* Check that atleast one column is defined */
        $ccount = count($ColDefs);
        if ($ccount == 0) return false; // <-- EXIT!
        
        /* Convert given definition to php's internal format */
        for ($c = 0; $c < $ccount; $c++) {
            $a = soExplode($ColDefs[$c], ":");
            switch (soCaseConv($a[1], "L")) {
                case "str":   $a[1] = "C"; break;
                case "bool":   $a[1] = "L"; break;
                case "memo":   $a[1] = "M"; break;
                case "date":   $a[1] = "D"; break;
                case "num":   $a[1] = "N"; break;
            } // switch

            $a[0] = strtoupper($a[0]);            
            $result[] = $a;
        } // for;
        
        $this->__Cols = $result;

        return true;
    } // setCols


    /** Write a row to database
     *  Replaces or adds a row in the table. If the row number is not given
     *  the row is added otherwise the row number given is replaced.
     *
     *  @param soSet $Values   Field names and values
     *  @param int $Pos    The row number where to write (must not be higher that
     *              the amount of rows in the table)
                    **** NOTE: this is one based.
     *  @return int    true if ok otherwise false
     *  @package sodbase
     */
    function writeRow($Values, $Pos = -1) {
        /* Check that table is ok */
        if ($this->__DBaseId == false) return false;  // <-- EXIT!

        /* Check that position number is not too large */
        if ($Pos > dbase_numrecords($this->__DBaseId)) return false; // <- EXIT!

        /* Construct array that have field values */
        $val_arr = array();
        $ccount = count($this->__Cols);
        for ($c = 0; $c < $ccount; $c++) {
            $row_def = $this->__Cols[$c];
            $col_name = $row_def[0];
            $val = $Values->getItem($col_name);
            $val_arr[] = $val; //$val;
        } // for $c

        /* Are we adding or replacing a row */
        if ($Pos == -1) {
                /* Add a new row */
                $result = dbase_add_record($this->__DBaseId, $val_arr);
                $this->__CurRec = dbase_numrecords($this->__DBaseId);
            } else {
                /* Replace an old row. */
                $result = dbase_replace_record($this->__DBaseId, $val_arr, $Pos);
                $this->__CurRec = $Pos;
            } // if $Pos

        return $result;
    } // writeRow


    /** Add a row to database
     *  Adds a row in the table.
     *
     *  @param array/any $Values Column values. The amount of the values
     *                           should correspond the amount of the columns
     *                           in the table.
     *  @return int    true if ok otherwise false
     *  @package sodbase
     */
    function addRow($Values) {
        /* Check that table is ok */
        if ($this->__DBaseId == false) return false;  // <-- EXIT!

        /* Add a new row */
        $result = dbase_add_record($this->__DBaseId, $Values);
        $this->__CurRec = dbase_numrecords($this->__DBaseId);

        return $result;
    } // addRow


    /** Reads a row from the database
     *  @param int $RowNo Row no. to read in.
     *  @return soSet Set that contains colum name/value pairs
     *  @package sodbase
     */
    function readRow($RowNo = -1) {
        /* Check that table is ok, cols defined and rowno in range */
        if ($this->__DBaseId == 0) return false;  // <-- EXIT!
        if (!$this->__Cols)  return 0;  // <-- EXIT!
        if ($RowNo == -1) $RowNo = $this->__CurRec;
        if ($RowNo > dbase_numrecords($this->__DBaseId)) return 0; // <- EXIT

        $res_arr = dbase_get_record($this->__DBaseId, $RowNo);
        /* Set the next row as current. */
        $this->__CurRec = $RowNo +1;

        /* Construct the result set */
        $result = new soSet();
        $ccount = count($this->__Cols);

        /* Check that there isn't defined too many fields */
        $field_count = dbase_numfields($this->__DBaseId);
        if ($ccount > $field_count)
            $ccount = $field_count;
        /* and construct. */
        for ($c = 0; $c < $ccount; $c++) {
            /* get the column information */
            $a = $this->__Cols[$c];
            /* Put the result in the result set. Key is the column name */
            $result->setItem($a[0], $res_arr[$c], 1);
        } // for $c


        return $result;
    } //readRow


    /** Closes the table
     *  @package sodbase
     */
    function closeTable() {
        /* Check that table is ok */
        if ($this->__DBaseId == false) return false;  // <-- EXIT!
        dbase_close($this->__DBaseId);
    } // closeTable


    /** Moves the record pointer
     *  @param int $RowNo Zero based pointer to the record to move. Must be
     *      lower that the amount of records
     *  @return boolean True if $RowNo in rabge and table is open, otherwise
     *      returns false.
     *  @package sodbase
     */
    function seek($RowNo) {
        /* Check that table is ok */
        if ($this->__DBaseId == false) return 0;  // <-- EXIT
        if ($RowNo > dbase_num_fields($this->__DBaseId)) return 0; // <-- EXIT

        $this->__CurRec = $RowNo;
        return 1;
    } // seek
    
    
    /** Destroy the table from the disk
     *  @package sodbase
     */
    function destroyTable() {
        /* Close and delete the table */
        $this->closeTable();
        unlink($this->Table);
    } // destroyTable
    
} // class  sodBase

?>
